/*:
 * @target MZ
 * @author sun
 * @plugindesc 随机地图
 * 
 * @help
 * 
 * 在插件参数中配置好全部参数，如果有空会报错
 * 
 * ！！迷宫地图需要在地图的note备注<RandomMap>！！
 * 
 * 在测试模式下，迷宫小地图默认会不显示迷雾，
 * tab键可以切换迷宫小地图 打开/关闭
 * 
 * 迷宫的地图内所有事件请使用独立开关控制，每次进入迷宫时会重置所有独立开关状态，
 * 迷宫内区域不同的相同id地图事件相互独立，需要使用独立开关控制。
 * 如果需要不可重复触发的事件，请使用变量/非独立开关，但是需要注意，如果使用变量
 * 或者非独立开关，哪怕重新进入迷宫，或是生成新的迷宫，该事件也不会改变状态。
 * 
 * 使用插件指令-创建迷宫- 生成迷宫
 * 
 * 使用插件指令-进入迷宫- 进入对应标签的迷宫
 * 
 * 使用插件指令-删除迷宫- 删除对应标签的迷宫
 * 
 * 创建迷宫时使用相同标签会覆盖之前的；
 * 所有迷宫数据会保存在$gameMap中；
 * 
 * ===========================================================================
 * 
 * Version - 1.01 - 20240131
 * 1.增加地图事件数量上限设置，防止随机事件生成过多
 * 
 * Version - 1.0 - 20240130
 * 1.正式版完成；
 * 2.修复了地图如果本身没有事件，事件id会设置错误的问题；
 * 3.修改了传送点事件的设置方式,现在请使用模板地图设置模板事件，然后再插件参数中填入事
 *   件id即可；
 * 4.增加了随机事件生成，生成规则在插件参数区域地图中设置；
 * 5.现在弱传送点不存在可以选择是否放置一个墙事件，使地图看上去更合理；
 * 
 * Version - 0.91 - 20240128
 * 1.修复了没有使用gf游戏核心依然会报错的问题；
 * 2.修复迷宫地图原有第一个事件会丢失的问题；
 * 3.修复了设置事件奖励地图后创建迷宫提示未设置的问题；
 * 4.修复了在迷宫地图内，各区块地图的独立开关不能独立生效的问题；
 * 5.修复了因编辑地图时，删除过事件导致事件id不连续，在某些情况下会造成地图data事件数组
 *   最后一个事件为null引起的传送点事件id设置错误，从而无法正确创建在地图中的问题；
 * 6.增加迷宫迷你地图功能，可以使用tab键打开/关闭；
 * 
 * Version - 0.9 - 20240127
 * 1.测试版发布；
 * 
 * ===========================================================================
 * @command Maker
 * @text 创建迷宫
 * @desc 根据你的参数创造一个迷宫
 * 
 * @arg Symbol
 * @type string
 * @text 索引标签
 * @desc 迷宫的索引
 * @default 陈安的秘密基地
 * 
 * @arg Width
 * @type number
 * @text 宽度
 * @desc 迷宫的宽度（区域）
 * @default 4
 * @max 20
 * 
 * @arg Height
 * @type number
 * @text 高度
 * @desc 迷宫的高度（区域）
 * @default 4
 * @max 20
 * 
 * @arg End X
 * @type number
 * @text 终点区域x
 * @desc 终点地图的区域坐标x
 * @default 3
 * 
 * @arg End Y
 * @type number
 * @text 终点区域y
 * @desc 终点地图的区域坐标y
 * @default 3
 * 
 * @arg End Level Map
 * @type number
 * @text 终点传送地图id
 * @desc 终点传送到目地图id
 * @default 1
 * 
 * @arg End Level X
 * @type number
 * @text 终点传送x
 * @desc 终点传送到目地图坐标x
 * @default 10
 * 
 * @arg End Level Y
 * @type number
 * @text 终点传送y
 * @desc 终点传送到目地图坐标y
 * @default 10
 * 
 * @arg Map List
 * @type number[]
 * @text 普通地图列表
 * @desc 迷宫生成会在以下地图中选取,填入的地图必须在插件参数中设置过,普通地图不限制每张地图出现次数,同地图id多次填入则会影响出现概率
 * @default []
 * 
 * @arg Reward Map List
 * @type number[]
 * @text 事件/奖励地图列表
 * @desc 迷宫生成会在以下地图中选取,填入的地图必须在插件参数中设置过,每张地图只能出现1次
 * @default []
 * 
 * @command Goto
 * @text 进入迷宫
 * @desc 进入一个创建好的迷宫
 * 
 * @arg Symbol
 * @type string
 * @text 索引标签
 * @desc 迷宫的索引
 * @default 
 * 
 * @arg X
 * @type number
 * @text 区域坐标x
 * @desc 区域坐标x
 * @default 0
 * 
 * @arg Y
 * @type number
 * @text 区域坐标Y
 * @desc 区域坐标Y
 * @default 0
 * 
 * @command Del
 * @text 删除迷宫
 * @desc 按索引删除迷宫
 * 
 * @arg Symbol
 * @type string
 * @text 索引标签
 * @desc 迷宫的索引
 * @default
 * 
 * @param Event Map
 * @text 事件地图
 * @type number
 * @default 0
 * @desc 存放生成事件的地图id
 * 
 * @param Map Part
 * @text 地图区域
 * @type struct<Map>[]
 * @default []
 * @desc 随机地图的拼接组件
 * 
 * @param Mini Map
 * @text 小地图设置
 * @type struct<MiniMap>
 * @default {"Default Enabled":"true","Location":"0","Width":"200","Height":"200","Deviation X":"0","Deviation Y":"0","Icon Id":"89","Opacity":"220","Fog Control":"0"}
 * @desc 小地图图参数设置
 * 
 * @param Event Max
 * @text 事件上限
 * @type number
 * @default 80
 * @desc 单地图事件上限，防止随机事件数量过多造成卡顿
 * 
 */
/*~struct~Map:
 * @param Map Id
 * @text 地图id
 * @type number
 * @default 1
 * @desc 地图id
 * 
 * @param Up Transfer
 * @text 上传送点
 * @type struct<Transfer>
 * @default {"Event Id":"0","Wall Event Id":"0","Position X":"0","Position Y":"0"}
 * @desc 设置传送点信息
 * 
 * @param Down Transfer
 * @text 下传送点
 * @type struct<Transfer>
 * @default {"Event Id":"0","Wall Event Id":"0","Position X":"0","Position Y":"0"}
 * @desc 设置传送点信息
 * 
 * @param Left Transfer
 * @text 左传送点
 * @type struct<Transfer>
 * @default {"Event Id":"0","Wall Event Id":"0","Position X":"0","Position Y":"0"}
 * @desc 设置传送点信息
 * 
 * @param Right Transfer
 * @text 右传送点
 * @type struct<Transfer>
 * @default {"Event Id":"0","Wall Event Id":"0","Position X":"0","Position Y":"0"}
 * @desc 设置传送点信息
 * 
 * @param End Transfer
 * @text 终点传送点/起始点
 * @type struct<Transfer>
 * @default {"Event Id":"0","Wall Event Id":"0","Position X":"0","Position Y":"0"}
 * @desc 设置传送点信息
 * 
 * @param Event List
 * @text 事件列表
 * @type struct<EventList>[]
 * @default []
 * @desc 随机事件生成设置
 */
/*~struct~Transfer:
 * @param Event Id
 * @text 事件id
 * @type number
 * @default 0
 * @desc 传送点事件在事件地图中的id，事件内容无需添加场景移动，会自动生成；
 * 
 * @param Wall Event Id
 * @text 墙事件id
 * @type number
 * @default 0
 * @desc 如果该位置没有传送点，则会放置此事件，可以根据地图图块专门制作墙的事件图，如果填0则不放置；
 * 
 * @param Position X
 * @text 坐标x
 * @type number
 * @default 0
 * @desc 事件x位置
 * 
 * @param Position Y
 * @text 坐标y
 * @type number
 * @default 0
 * @desc 事件y位置
 */
/*~struct~MiniMap:
 * @param Default Enabled
 * @text 默认启用
 * @type boolean
 * @default true
 * @desc 是否默认启用小地图
 * 
 * @param Location
 * @text 位置
 * @type number
 * @default 0
 * @desc 小地图位于屏幕位置：0-左上  1-右上  2-左下  3-右下
 * 
 * @param Width
 * @text 小地图宽
 * @type number
 * @default 200
 * @desc 小地图的高度
 * 
 * @param Height
 * @text 小地图高
 * @type number
 * @default 200
 * @desc 小地图的宽度
 * 
 * @param Deviation X
 * @text 小地图x偏移
 * @type number
 * @default 0
 * @desc 小地图位置坐标横向偏移距离
 * 
 * @param Deviation Y
 * @text 小地图y偏移
 * @type number
 * @default 0
 * @desc 小地图位置坐标横向偏移距离
 * 
 * @param Icon Id
 * @text 图标id
 * @type number
 * @default 89
 * @desc 显示当前位置的图标
 * 
 * @param Opacity
 * @text 不透明度
 * @type number
 * @default 220
 * @max 255
 * @desc 小地图不透明度
 * 
 * @param Fog Control
 * @text 迷雾控制
 * @type switch
 * @default 0
 * @desc 在测试模式或x开关打开的情况下，小地图全开
 */
/*~struct~EventList:
 * @param Symbol
 * @text 事件标志
 * @type string
 * @default 
 * @desc 该事件的唯一识别标签
 * 
 * @param Event Id
 * @text 事件id
 * @type number
 * @default 0
 * @desc 事件在事件地图中的id
 * 
 * @param Region Id
 * @text 区域id
 * @type number
 * @default 0
 * @max 255
 * @desc 该事件只会在设置的区域内生成
 * 
 * @param Generate Rate
 * @text 产生概率
 * @type int
 * @default 0
 * @min 0
 * @max 1
 * @desc 该事件产生的几率(0-1]
 */
var Imported = Imported || {};
Imported.sun_RandomMap = true;

/**
 * 随机地图插件相关变量
 */
const sun_romMap = {};

/**
 * 处理插件参数
 */
(function () {
    const pluginParam = PluginManager.parameters('sun_RandomMap');
    const mapConfig = JSON.parse(pluginParam["Map Part"]);
    const data = {};
    for (const config of mapConfig) {
        const obj = JSON.parse(config);
        const mapInfo = Object.keys(obj);
        const mapId = obj["Map Id"];
        for (const config of mapInfo) {
            if (!obj[config]) {
                alert(`插件参数设置出错，请检查！`);
            }
            obj[config] = JSON.parse(obj[config]);
            if (config === "Event List") {
                obj["Event List"] = obj["Event List"].map((item) => {
                    return JSON.parse(item);
                })
            }
        }
        data[mapId] = obj;
    }
    sun_romMap.eventMapId = Number(pluginParam["Event Map"]);
    sun_romMap.mapConfig = data;
    sun_romMap.miniMapData = JSON.parse(pluginParam["Mini Map"]);
    sun_romMap.eventMaxNum = Number(pluginParam["Event Max"]);

})()

/**
 * 数据库增加模板事件地图data文件
 */
DataManager._databaseFiles.push({ name: "$dataEvents", src: `Map${sun_romMap.eventMapId.padZero(3)}.json` })

/**
 * 随机迷宫地图的墙类
 * @class
 */
class RandomMap_Wall {
    constructor() {
        this.break = false;//墙壁是否被打通
        this.pass = [];//墙两边的地图区域数组
    }
}
/**
 * 随机迷宫地图的图块类
 * @class
 */
class RandomMap_Tile {
    constructor(x, y, index) {
        //墙
        this.wall = {
            "up": null,
            "down": null,
            "left": null,
            "right": null
        }
        this.x = x;//在整个地图中的坐标位置x
        this.y = y;//在整个地图中的坐标位置y
        this.index = index;//唯一索引
        this.tile = [];//??干嘛用的来着
        this.isPass = false;//是否已经经过
        this.baseSeed = NaN//基础随机数种子
        this._currentSeed = NaN//当前随机数种子
    }

    /**
     * 设置地图
     */
    setMap(mapId) {
        this.mapId = mapId;
        this.baseSeed = Math.randomInt(1000000);
    }

    /**
     * 创建本地图区域四边墙壁
     * @param {RandomMap} moze 随机地图对象
     */
    createWall(moze) {
        this.createUp(moze);
        this.createDown(moze);
        this.createLeft(moze);
        this.createRight(moze);
    }

    /**
     * 创建上墙壁
     * @param {RandomMap} moze 随机地图对象
     */
    createUp(moze) {
        const upX = this.x - 1;
        if (upX >= 0) {
            const upTile = moze.data[upX][this.y];
            const upTileDownWall = upTile.wall.down;
            if (upTileDownWall) {
                upTileDownWall.pass.push(this);
                this.wall.up = upTileDownWall;
                return
            }
        }
        const wall = new RandomMap_Wall();
        moze.wall.push(wall);
        wall.pass.push(this);
        this.wall.up = wall;
    }

    /**
     * 创建下墙壁
     * @param {RandomMap} moze 随机地图对象
     */
    createDown(moze) {
        const downX = this.x + 1;
        if (downX < moze.height) {
            const downTile = moze.data[downX][this.y];
            const downTileUpWall = downTile.wall.up;
            if (downTileUpWall) {
                downTileUpWall.pass.push(this);
                this.wall.down = downTileUpWall;
                return
            }
        }
        const wall = new RandomMap_Wall();
        moze.wall.push(wall);
        wall.pass.push(this);
        this.wall.down = wall;
    }

    /**
     * 创建左墙壁
     * @param {RandomMap} moze 随机地图对象
     */
    createLeft(moze) {
        const leftY = this.y - 1;
        if (leftY >= 0) {
            const leftTile = moze.data[this.x][leftY];
            const leftTileRightWall = leftTile.wall.right;
            if (leftTileRightWall) {
                leftTileRightWall.pass.push(this);
                this.wall.left = leftTileRightWall;
                return
            }
        }
        const wall = new RandomMap_Wall();
        moze.wall.push(wall);
        wall.pass.push(this);
        this.wall.left = wall;
    }

    /**
     * 创建右墙壁
     * @param {RandomMap} moze 随机地图对象
     */
    createRight(moze) {
        const rightY = this.y + 1;
        if (rightY < moze.width) {
            const rightTile = moze.data[this.x][rightY];
            const rightTileLeftWall = rightTile.wall.left;
            if (rightTileLeftWall) {
                rightTileLeftWall.pass.push(this);
                this.wall.right = rightTileLeftWall;
                return
            }
        }
        const wall = new RandomMap_Wall();
        moze.wall.push(wall);
        wall.pass.push(this);
        this.wall.right = wall;
    }

    /**
     * 获取图块的配置信息
     * @returns {object} 当前图块的配置信息
     */
    getConfig() {
        return sun_romMap.mapConfig[this.mapId];
    }

    resetSeed() {
        this._currentSeed = this.baseSeed;
    }

    random() {
        this._currentSeed = (this._currentSeed * 9301 + 49297) % 233280;
        return this._currentSeed / 233280.0;
    }
}

/**
 * 随机地图类-
 * 目前只能生成迷宫
 * @class
 */
class RandomMap {
    constructor(width, height, mapList, rewardList) {
        this.data = [];//所有地图区域
        this.wall = [];//所有的墙
        this.x = 0;//当前位置x
        this.y = 0;//当前位置y
        this.width = width;//地图宽
        this.height = height;//地图高
        this.bitmap = new Bitmap(width * 32, height * 32);//位图，调试用
        this.util = new UnionFind(this.width * this.height);//判通工具，并查集
        this.end = [0, 0];//终点坐标
        this.mapList = mapList;//普通地图生成范围
        this.rewardList = rewardList;//事件、奖励关地图生成范围
        this.switche = [];//迷宫独立开关
        this.mapSwitche = new Map();//迷宫区域地图独立开关
        this.mainWorldSwitche = {};//拷贝主世界开关的存储变量
    }

    /**
     * 创建迷宫
     */
    createMoze() {
        this.createTile();
        this.createWall();
        this.breakWall();
        this.rollMap();
    }

    /**
     * 创建所有地图区域
     */
    createTile() {
        const { width, height } = this;
        let index = 0;
        for (let i = 0; i < width; i++) {
            const row = [];
            for (let j = 0; j < height; j++) {
                const tile = new RandomMap_Tile(i, j, index);
                index++;
                row.push(tile);
            }
            this.data.push(row);
        }
    }

    /**
     * 创建所有墙
     */
    createWall() {
        const { width, height } = this;
        for (let i = 0; i < width; i++) {
            for (let j = 0; j < height; j++) {
                const tile = this.data[i][j];
                tile.createWall(this);
            }
        }
    }

    /**
     * Kruskal算法，
     * 打断墙使两边区域连通的方式生成迷宫
     */
    breakWall() {
        let count = 0;
        const list = this.getList();
        while (list.length > 0) {
            //防止奇怪的情况出现死循环
            if (count > 100000) return count;
            count++;
            //从所有可打断墙中抽取一个墙
            const index = list[Math.randomInt(list.length)];
            const wall = this.wall[index];
            //墙两边区域的图块，因为抽取范围是所有内墙，所以一定有2个区域
            const tile1 = wall.pass[0];
            const tile2 = wall.pass[1];
            //并查集判断两边区域是否连通，如果不通则打断墙
            if (this.util.find(tile1.index) !== this.util.find(tile2.index)) {
                this.util.union(tile1.index, tile2.index)
                wall.break = true;
            }
            //将该墙从列表中移除
            list.remove(index);
        }
    }

    /**
     * 为所有图块抽取地图
     */
    rollMap() {
        //将图块二维数组展开
        const allTile = this.data.flat();
        //先抽奖励区域
        for (const mapId of this.rewardList) {
            //如果事件奖励地图数量大于迷宫总区域数，则所有区块都被分配地图后跳出函数
            if (allTile.length <= 0) return;
            const index = Math.randomInt(allTile.length);
            const tile = allTile[index];
            tile.setMap(mapId);
            allTile.remove(tile);
        }
        //奖励区域清空后剩余图块随机分配地图
        for (const tile of allTile) {
            const index = Math.randomInt(this.mapList.length);
            const mapId = this.mapList[index];
            tile.setMap(mapId);
        }
    }

    /**
     * 设置终点
     * @param {number} x 终点区域x坐标
     * @param {number} y 终点区域y坐标
     */
    setEnd(x, y) {
        this.end = [x, y];
    }

    /**
     * 设置终点出去的目标地图数据
     */
    setEndLevelMap(tid, tx, ty) {
        this.endLevelMap = [tid, tx, ty];
    }

    /**
     * 当前位置是否是终点
     * @returns {Boole} 当前位置是否是终点
     */
    isEnd() {
        return this.location().toString() === this.end.toString();
    }

    /**
     * 得到迷宫所有内墙的索引
     * @returns {number[]} 内墙索引数组
     */
    getList() {
        const list = [];
        for (let i = 0; i < this.wall.length; i++) {
            if (this.wall[i].pass.length === 2) {
                list.push(i);
            }
        }
        return list;
    }

    /**
     * 绘制小地图精灵位图
     */
    draw() {
        const w = Number(sun_romMap.miniMapData["Width"]);
        const h = Number(sun_romMap.miniMapData["Height"]);
        const switchId = Number(sun_romMap.miniMapData["Fog Control"]);
        const iconIndex = Number(sun_romMap.miniMapData["Icon Id"]);
        const bitmap = new Bitmap(w, h);
        const iconBitmap = ImageManager.loadSystem("IconSet");
        const tileWidth = Math.floor(w / this.width);//区域图块宽度
        const tileHeigth = Math.floor(w / this.width);//区域图块高度
        //画背景
        bitmap.fillRect(0, 0, bitmap.width, bitmap.height, "#2D2D2D");
        for (let i = 0; i < this.width; i++) {
            for (let j = 0; j < this.height; j++) {
                const tile = this.data[i][j];
                const ox = j * tileWidth;
                const oy = i * tileHeigth;
                const c = "#FFFFFF";
                const wall = tile.wall;
                //如果已经经过或满足显示条件
                if (tile.isPass || (Utils.isNwjs() && Utils.isOptionValid("test")) || $gameSwitches.value(switchId)) {
                    //先画墙
                    if (!wall.up.break) {
                        bitmap.fillRect(ox, oy, tileWidth, 2, c);
                    }
                    if (!wall.down.break) {
                        bitmap.fillRect(ox, oy + tileHeigth - 2, tileWidth, 2, c);
                    }
                    if (!wall.left.break) {
                        bitmap.fillRect(ox, oy, 2, tileHeigth, c);
                    }
                    if (!wall.right.break) {
                        bitmap.fillRect(ox + tileWidth - 2, oy, 2, tileHeigth, c);
                    }
                }
                //画自己
                if (i === this.x && j === this.y) {
                    const pw = ImageManager.iconWidth;
                    const ph = ImageManager.iconHeight;
                    const sx = (iconIndex % 16) * pw;
                    const sy = Math.floor(iconIndex / 16) * ph;
                    const ix = ox + (tileWidth - pw) / 2;
                    const iy = oy + (tileHeigth - ph) / 2;
                    bitmap.blt(iconBitmap, sx, sy, pw, ph, ix, iy);
                }
            }
        }
        return bitmap;
    }

    /**
     * 获取当前位置的图块
     * @returns {RandomMap_Tile} 图块对象
     */
    getTile() {
        return this.data[this.x][this.y];
    }

    /**
     * 获取图块上方的图块对象；
     * 如果迷宫生成不出错的话，整个迷宫外墙不会出现打破状态，故以下4个方法不判断边界
     * @param {RandomMap_Tile} tile 图块对象
     * @returns {RandomMap_Tile} 图块对象
     */
    getUpTile(tile) {
        return this.data[tile.x - 1][tile.y];
    }

    /**
     * 获取图块下方的图块对象
     * @param {RandomMap_Tile} tile 图块对象
     * @returns {RandomMap_Tile} 图块对象
     */
    getDownTile(tile) {
        return this.data[tile.x + 1][tile.y];
    }

    /**
     * 获取图块左方的图块对象
     * @param {RandomMap_Tile} tile 图块对象
     * @returns {RandomMap_Tile} 图块对象
     */
    getLeftTile(tile) {
        return this.data[tile.x][tile.y - 1];
    }

    /**
     * 获取图块右方的图块对象
     * @param {RandomMap_Tile} tile 图块对象
     * @returns {RandomMap_Tile} 图块对象
     */
    getRightTile(tile) {
        return this.data[tile.x][tile.y + 1];
    }

    /**
     * 获取当前位置坐标
     * @returns {number[]} 坐标
     */
    location() {
        return [this.x, this.y];
    }

    /**
     * 获取当前图块唯一索引
     * @returns {number} 索引
     */
    tileIndex() {
        return this.getTile().index;
    }

    /**
     * 重置迷宫所有开关状态
     */
    clearSwitcher() {
        $gameSelfSwitches._data = this.mainWorldSwitche;
        this.mainWorldSwitche = {};
    }

    /**
     * 保存主世界开关
     */
    saveMainWorldSwitche() {
        this.mainWorldSwitche = $gameSelfSwitches._data;
    }
}

/**
 * 并查集：
 * 工具类，用于生成迷宫是判断通路
 * @class
 */
class UnionFind {
    constructor(size) {
        this.parents = Array(size)
            .fill(0)
            .map((_, i) => i);    // 存储元素父节点信息
        this.ranks = Array(size).fill(1);    // 存储元素秩信息
    }
    find(x) {    // 查询操作
        if (x !== this.parents[x]) {
            this.parents[x] = this.find(this.parents[x]);    // 递归并进行路径压缩
        }
        return this.parents[x];
    }
    union(x, y) {    // 合并操作
        const px = this.find(x);
        const py = this.find(y);
        if (px === py) return;
        // 按秩合并
        if (this.ranks[px] > this.ranks[py]) {
            this.parents[py] = px;
            this.ranks[px] += this.ranks[py];
        } else {
            this.parents[px] = py;
            this.ranks[py] += this.ranks[px];
        }
    }
}

/**
 * Game_Map初始化；
 * 增加存储随机地图变量
 */
sun_romMap.map_initialize = Game_Map.prototype.initialize;
Game_Map.prototype.initialize = function () {
    sun_romMap.map_initialize.call(this);
    this._randomMapList = {};
    this._randomMapIndex = undefined;
}

/**
 * 设置参数；
 * 增加当前地图是否是迷宫地图
 */
sun_romMap.map_setup = Game_Map.prototype.setup
Game_Map.prototype.setup = function (mapId) {
    if ($dataMap.meta.RandomMap) {
        this._isMoze = true;
        //将当前图块设置为已经过
        this.moze().getTile().isPass = true;
        //将独立开关更新为迷宫区块开关
        this.updateMozeSwitches();
    } else {
        this._isMoze = false;
        //恢复大世界开关
        this.moze()?.clearSwitcher();
        this._randomMapIndex = undefined;
    }
    sun_romMap.map_setup.call(this, mapId);
}
/**
 * 更新迷宫区域地图独立开关
 */
Game_Map.prototype.updateMozeSwitches = function () {
    const moze = this.moze();
    const mapSwitches = moze.mapSwitche;
    const index = moze.tileIndex();
    if (!mapSwitches.has(index)) {
        mapSwitches.set(index, {})
    }
    $gameSelfSwitches._data = mapSwitches.get(index);
}

/**
 * 设置所有事件；
 * 增加迷宫传送点以及终点事件
 */
sun_romMap.setupEvents = Game_Map.prototype.setupEvents;
Game_Map.prototype.setupEvents = function () {
    if (this._isMoze) {
        this.setupMozeEvent();
    }
    sun_romMap.setupEvents.call(this);
}

/**
 * 生成迷宫
 * @param {string} symbol 迷宫唯一引值
 * @param {number} w 要生成的迷宫宽
 * @param {number} h 要生成的迷宫高
 * @param {number[]} mapList 可选地图列表
 * @param {number[]} rewardList 可选奖励地图列表
 */
Game_Map.prototype.createMoze = function (symbol, w, h, mapList, rewardList) {
    const moze = new RandomMap(w, h, mapList, rewardList);
    if (!this._randomMapList) this._randomMapList = this._randomMapList;
    this._randomMapList[symbol] = moze;
    //设置当前迷宫索引
    this._randomMapIndex = symbol;
    moze.createMoze();
}


/**
 * 进入迷宫
 * @param {string} symbol 迷宫唯一引值 
 * @param {number} x 区域坐标x
 * @param {number} y 区域坐标y
 */
Game_Map.prototype.gotoMoze = function (symbol, x, y) {
    //设置当前迷宫索引
    this._randomMapIndex = symbol;
    const moze = this.moze();
    if (moze) {
        moze.x = x;
        moze.y = y;
        const tile = moze.getTile();
        const mapId = tile.mapId;
        const config = tile.getConfig();
        const ox = Number(config["End Transfer"]["Position X"]);
        const oy = Number(config["End Transfer"]["Position Y"]);
        $gamePlayer.reserveTransfer(mapId, ox, oy, 0, 0);
    }
}

/**
 * 删除迷宫
 * @param {string} symbol 迷宫唯一引值 
 */
Game_Map.prototype.delMoze = function (symbol) {
    if (this._randomMapList[symbol]) {
        this._randomMapList[symbol] = null;
    }
}

/**
 * 获取当前迷宫对象
 * @returns {RandomMap} 当前迷宫对象
 */
Game_Map.prototype.moze = function () {
    return this._randomMapList[this._randomMapIndex];
}

/**
 * 制作传送点数据
 * @param {string} type 类型
 * @param {boolean} state 打破状态
 * @returns {object} 事件数据
 */
Game_Map.prototype.makeTransferData = function (type, state) {
    const moze = this.moze();
    const tile = moze.getTile();
    const userConfig = tile.getConfig();
    let sameName, oppositeName, js;
    //根据墙类型获得正确的key
    switch (type) {
        case "up":
            sameName = "Up";
            oppositeName = "Down";
            js = ["$gameMap.moze().x--"];
            break;
        case "down":
            sameName = "Down";
            oppositeName = "Up";
            js = ["$gameMap.moze().x++"];
            break;
        case "left":
            sameName = "Left";
            oppositeName = "Right";
            js = ["$gameMap.moze().y--"];
            break;
        case "right":
            sameName = "Right";
            oppositeName = "Left";
            js = ["$gameMap.moze().y++"];
            break;
        default:
            break;
    }
    //事件坐标
    const x = Number(userConfig[`${sameName} Transfer`]["Position X"]);
    const y = Number(userConfig[`${sameName} Transfer`]["Position Y"]);
    //如果未打破，则返回墙事件数据
    if (!state) {
        //模板事件id
        const mouldId = Number(userConfig[`${sameName} Transfer`]["Wall Event Id"]);
        const data = { mouldId, x, y };
        return data;
    }
    const targetTile = moze[`get${sameName}Tile`](tile);
    const targeConfig = targetTile.getConfig();
    //模板事件id
    const mouldId = Number(userConfig[`${sameName} Transfer`]["Event Id"]);
    //如果该id事件未设置,返回undefined
    if (!$dataEvents.events[mouldId]) return undefined;
    //传动目标位置
    const targetMap = targetTile.mapId;
    const targetX = Number(targeConfig[`${oppositeName} Transfer`]["Position X"]);
    const targetY = Number(targeConfig[`${oppositeName} Transfer`]["Position Y"]);
    //===========================================================================================
    const data = { mouldId, x, y, targetMap, targetX, targetY, js };
    return data;
}

/**
 * 设置迷宫事件
 */
Game_Map.prototype.setupMozeEvent = function () {
    const tile = this.moze().getTile();
    //放置墙或传送点
    const { wall } = tile;
    //事件id：去掉null，取最后一个元素获取id，并在此基础上+1，如果原地图没有事件则取1
    let eventId = ($dataMap.events.filter((item) => {
        return item !== null;
    }).slice(-1)[0]?.id ?? 0) + 1;
    //保存时间data中null的数量，用于后续判断地图事件数量
    $gameTemp.eventNullNum = $dataMap.events.filter((item) => {
        return item === null;
    }).length;

    for (const w of Object.keys(wall)) {
        const data = this.makeTransferData(w, wall[w].break);
        if (data) {
            data.eventId = eventId;
            //制作事件data
            this.makeEventData(data);
            eventId++
        }
    }
    //终点
    if (this.moze().isEnd()) {
        this.setupEndEvent(eventId);
        eventId++;
    }
    //设置随机事件
    this.setupRandomEvent(eventId);
}

/**
 * 设置迷宫终点事件
 * @param {number} eventId 
 */
Game_Map.prototype.setupEndEvent = function (eventId) {
    const config = this.moze().getTile().getConfig();
    const mouldId = Number(config["End Transfer"]["Event Id"]);
    const x = Number(config["End Transfer"]["Position X"]);
    const y = Number(config["End Transfer"]["Position Y"]);
    const targetMap = this.moze().endLevelMap[0];
    const targetX = this.moze().endLevelMap[1];
    const targetY = this.moze().endLevelMap[2];
    const js = [""];
    const data = { eventId, mouldId, x, y, targetMap, targetX, targetY, js };
    //制作事件data
    this.makeEventData(data);
}

/**
 * 设置迷宫随机事件
 * @param {number} eventId 起始事件id
 */
Game_Map.prototype.setupRandomEvent = function (eventId) {
    const tile = this.moze().getTile();
    const config = tile.getConfig()["Event List"];
    const list = this.getMapRegionData();
    tile.resetSeed();
    for (const event of config) {
        const region = Number(event["Region Id"]);
        const tileList = list[region];
        if (tileList) {
            const mouldId = Number(event["Event Id"]);
            const rate = Number(event["Generate Rate"]);
            for (const t of tileList) {
                if (eventId - $gameTemp.eventNullNum > sun_romMap.eventMaxNum) return;
                const random = tile.random();
                if (rate > random) {
                    const x = t[0];
                    const y = t[1];
                    const data = { eventId, mouldId, x, y };
                    this.makeEventData(data);
                    eventId++;
                }
            }
        }
    }
}

/**
 * 制作迷宫事件数据
 * @param {object} data 事件data 
 */
Game_Map.prototype.makeEventData = function (data) {
    const { eventId, mouldId, x, y, targetMap, targetX, targetY, js } = data;
    if (mouldId === 0) return;
    const parameters = [0, targetMap, targetX, targetY, 0, 0];
    const eventData = JSON.parse(JSON.stringify($dataEvents.events[mouldId]));
    const jsEventData = {
        "code": 355,
        "indent": 0,
        "parameters": js
    }
    const moveEventData = {
        "code": 201,
        "indent": 0,
        "parameters": parameters
    }
    eventData.id = eventId;
    eventData.x = x;
    eventData.y = y;
    //如果目标地图参数存在，则说明是传送点，为其添加js与场所移动事件
    if (targetMap) {
        //添加脚本事件，用于更改迷宫当前位置坐标
        eventData.pages[0].list.unshift(jsEventData);
        //添加场所移动事件
        eventData.pages[0].list.splice(-1, 0, moveEventData);
    }
    $dataMap.events[eventId] = eventData;
    //解决未知原因出现的事件data丢失bug，由于该bug无法稳定且很难复现，此为暂时解决方案
    //将所有脚本添加的事件data临时存储到$gameTemp中
    $gameTemp.mozeEventData ||= {};
    $gameTemp.mozeEventData[eventId] = eventData;
}
//对应上面的未知bug，如果找不到事件data则从$gameTemp中读取
sun_romMap.event = Game_Event.prototype.event;
Game_Event.prototype.event = function () {
    if (!$dataMap.events[this._eventId]) {
        return $gameTemp.mozeEventData[this._eventId]
    }
    return sun_romMap.event.call(this);
}

/**
 * 得到当前地图图块区域信息
 * @returns {object} 地图设置了区域的图块列表
 */
Game_Map.prototype.getMapRegionData = function () {
    const list = {};
    const width = $dataMap.width;
    const height = $dataMap.height;
    const num = width * height;
    const regionData = $dataMap.data.slice(num * 5, num * 6);
    for (let i = 0; i < regionData.length; i++) {
        if (regionData[i] !== 0) {
            list[regionData[i]] ??= [];
            list[regionData[i]].push([i % width, Math.floor(i / width)]);
        }
    }
    return list;
}

/**
 * 传送；
 * 增加如果当前在迷宫中就要刷新地图
 */
Game_Player.prototype.performTransfer = function () {
    if (this.isTransferring()) {
        this.setDirection(this._newDirection);
        if (this._newMapId !== $gameMap.mapId() || this._needsMapReload || $gameMap._isMoze) {
            $gameMap.setup(this._newMapId);
            this._needsMapReload = false;
        }
        this.locate(this._newX, this._newY);
        this.refresh();
        this.clearTransferInfo();
    }
}

/**
 * 独立开关；
 * 记录处于迷宫中的独立开关改变，进入迷宫时重置开关
 */
sun_romMap.setValue = Game_SelfSwitches.prototype.setValue
Game_SelfSwitches.prototype.setValue = function (key, value) {
    if ($gameMap._isMoze) {
        const moze = $gameMap.moze();
        const index = moze.tileIndex();
        const mapSwitche = moze.mapSwitche;
        //记录迷宫中每个区块的开关，用于识别相同地图id但是在迷宫中不同区域的地图事件状态
        mapSwitche.get(index)[key] = value;
    }
    sun_romMap.setValue.apply(this, arguments)
}

/**
 * 更新；
 * 增加tap键开启关闭迷宫迷你地图
 */
sun_romMap.Scene_Map_update = Scene_Map.prototype.update;
Scene_Map.prototype.update = function () {
    sun_romMap.Scene_Map_update.call(this);
    if (Input.isTriggered("tab") && this._mozeMiniMap) {
        this._mozeMiniMap.visible = !this._mozeMiniMap.visible;
    }
}

/**
 * 创建显示对象；
 * 增加迷宫小地图
 */
sun_romMap.createDisplayObjects = Scene_Map.prototype.createDisplayObjects;
Scene_Map.prototype.createDisplayObjects = function () {
    sun_romMap.createDisplayObjects.call(this);
    if ($gameMap._isMoze) {
        this.drawMiniMap();
    }
}

/**
 * 绘制迷宫小地图
 */
Scene_Map.prototype.drawMiniMap = function () {
    const config = sun_romMap.miniMapData;
    const open = config["Default Enabled"] === "true";
    if (!open) return;
    const w = Number(config["Width"]);
    const h = Number(config["Height"]);
    const l = Number(config["Location"]);
    const dx = Number(config["Deviation X"]);
    const dy = Number(config["Deviation Y"]);
    const opacity = Number(config["Opacity"]);
    const moze = $gameMap.moze();
    let x, y;
    switch (l) {
        case 1:
            x = Graphics.boxWidth - w;
            y = 0;
            break;
        case 2:
            x = o;
            y = Graphics.boxHeight - h;
            break;
        case 3:
            x = Graphics.boxWidth - w;
            y = Graphics.boxHeight - h;
            break;
        default:
            x = 0;
            y = 0;
            break;
    }
    x += dx;
    y += dy;
    const sprite = new Sprite(moze.draw());
    sprite.move(x, y);
    sprite.opacity = opacity;
    this.addChild(sprite);
    this._mozeMiniMap = sprite;
}

/**
 * =================================插件指令=================================
 */

/**
 * 创建迷宫
 */
PluginManager.registerCommand("sun_RandomMap", "Maker", (arg) => {
    const symbol = arg.Symbol;
    const w = Number(arg.Width);
    const h = Number(arg.Height);
    const px = Number(arg["End X"]);
    const py = Number(arg["End Y"]);
    const tid = Number(arg["End Level Map"]);
    const tx = Number(arg["End Level X"]);
    const ty = Number(arg["End Level Y"]);
    const mapRange = Object.keys(sun_romMap.mapConfig).map(Number);
    const mapList = JSON.parse(arg["Map List"]).map(Number);
    const reward = JSON.parse(arg["Reward Map List"]).map(Number);
    //制作生成范围
    for (let i = 0; i < mapList.length; i++) {
        if (!mapRange.includes(mapList[i])) {
            alert(`地图${mapList[i]}未设置，生成失败`);
            return;
        }
        mapList[i] = Number(mapList[i]);
    }
    for (let i = 0; i < reward.length; i++) {
        if (!mapRange.includes(reward[i])) {
            alert(`地图${reward[i]}未设置，生成失败`);
            return;
        }
        reward[i] = Number(reward[i]);
    }
    //生成迷宫
    $gameMap.createMoze(symbol, w, h, mapList, reward);
    const moze = $gameMap.moze();
    moze.setEnd(px, py);
    moze.setEndLevelMap(tid, tx, ty);
});

/**
 * 进入迷宫
 */
PluginManager.registerCommand("sun_RandomMap", "Goto", (arg) => {
    const symbol = arg.Symbol;
    const x = Number(arg.X);
    const y = Number(arg.Y);
    const moze = $gameMap.moze();
    //保存主世界开关
    moze.saveMainWorldSwitche();
    $gameMap.gotoMoze(symbol, x, y);
});

/**
 * 删除迷宫
 */
PluginManager.registerCommand("sun_RandomMap", "Del", (arg) => {
    const symbol = arg.Symbol;
    $gameMap.delMoze(symbol);
});